#include "rtu_slave.hpp"

#include "slave_common.hpp"

namespace dcs {


    rtu_slave_t::rtu_slave_t(const log_handler_t& log_handler)
        : log_handler_(log_handler)
    {
    }

    rtu_slave_t::~rtu_slave_t()
    {
    }

    bool rtu_slave_t::start(const std::string& device, int baudrate, char parity, int data_bit, int stop_bit, std::uint32_t registers_num) {
        assert(!device.empty());
        assert(baudrate > 0 && baudrate <= 115200);
        assert(data_bit >= 5 && data_bit <= 8);
        assert(stop_bit == 1 || stop_bit == 2);

        ctx_ = ::modbus_new_rtu(device.c_str(), baudrate, parity, data_bit, stop_bit);
        if (ctx_ == nullptr)
            return false;

        if (::modbus_set_slave(ctx_, id_) == -1) {
            return false;
        }

        if (::modbus_connect(ctx_) == -1) {
            return false;
        }


        //::modbus_set_debug(ctx_, 1);
        ::modbus_set_error_recovery(ctx_, MODBUS_ERROR_RECOVERY_PROTOCOL);

        buffer_ = ::modbus_mapping_new(0, 0, registers_num, 0);
        if (buffer_ == nullptr)
            return false;

        recv_thr_ = std::make_unique<std::jthread>([this](std::stop_token st) {
            while (!st.stop_requested()) {
                handle();
            }
            });

        return true;

    }

    void rtu_slave_t::stop() {

        ::modbus_close(ctx_);

        if(recv_thr_)
            recv_thr_.reset();

        if (ctx_ == nullptr)
            return;
        if(buffer_ != nullptr)
            ::modbus_mapping_free(buffer_);

    
        ::modbus_free(ctx_);
    }

    bool rtu_slave_t::handle() {
        if (ctx_ == nullptr)
            return false;

        std::uint8_t query[MODBUS_RTU_MAX_ADU_LENGTH] = { 0 };
        auto query_len = ::modbus_receive(ctx_, query);
        if (query_len < 0)
            return false;

        if (query_len == 0)
            return false;

        auto ret = ::modbus_reply(ctx_, query, query_len, buffer_);
        return ret >= 0;
    }

    void rtu_slave_t::update(std::time_t time, std::uint16_t sampling_pt, const std::vector<float>& params) {
        if (buffer_ == nullptr)
            return;

        buffer_update(buffer_, sampling_pt, time, params, {});
    }

}